//------------------------------------------------------------------------------
// <copyright file="TimeoutTimer.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
//  Class used to manage timeouts in complex system operations.
//
// <owner current="true" primary="true">Microsoft</owner>
// <owner current="true" primary="false">Microsoft</owner>
//------------------------------------------------------------------------------

namespace System.Data.ProviderBase
{
    using System;
    using System.Data.Common;
    using System.Diagnostics;

// Purpose:
//   Manages determining and tracking timeouts
//
// Intended use:
//   Call StartXXXXTimeout() to get a timer with the given expiration point
//   Get remaining time in appropriate format to pass to subsystem timeouts
//   Check for timeout via IsExpired for checks in managed code.
//   Simply abandon to GC when done.
internal class TimeoutTimer
{
    //-------------------
    // Fields
    //-------------------
    private long _timerExpire;
    private bool _isInfiniteTimeout;
    private long _originalTimerTicks;

    //-------------------
    // Timeout-setting methods
    //-------------------

    // Get a new timer that will expire in the given number of seconds
    //  For input, a value of zero seconds indicates infinite timeout
    internal static TimeoutTimer StartSecondsTimeout(int seconds)
    {
        //--------------------
        // Preconditions: None (seconds must conform to SetTimeoutSeconds requirements)

        //--------------------
        // Method body
        var timeout = new TimeoutTimer();
        timeout.SetTimeoutSeconds(seconds);

        //---------------------
        // Postconditions
        Debug.Assert(timeout != null); // Need a valid timeouttimer if no error

        return timeout;
    }

    // Get a new timer that will expire in the given number of milliseconds
    //  No current need to support infinite milliseconds timeout
    internal static TimeoutTimer StartMillisecondsTimeout(long milliseconds)
    {
        //--------------------
        // Preconditions
        Debug.Assert(0 <= milliseconds);

        //--------------------
        // Method body
        var timeout = new TimeoutTimer();
        timeout._originalTimerTicks = milliseconds * TimeSpan.TicksPerMillisecond;
        timeout._timerExpire = checked(ADP.TimerCurrent() + timeout._originalTimerTicks);
        timeout._isInfiniteTimeout = false;

        //---------------------
        // Postconditions
        Debug.Assert(timeout != null); // Need a valid timeouttimer if no error

        return timeout;
    }

    //-------------------
    // Methods for changing timeout
    //-------------------

    internal void SetTimeoutSeconds(int seconds)
    {
        //--------------------
        // Preconditions
        Debug.Assert(0 <= seconds || InfiniteTimeout == seconds);  // no need to support negative seconds at present

        //--------------------
        // Method body
        if (InfiniteTimeout == seconds)
        {
            _isInfiniteTimeout = true;
        }
        else
        {
            // Stash current time + timeout
            _originalTimerTicks = ADP.TimerFromSeconds(seconds);
            _timerExpire = checked(ADP.TimerCurrent() + _originalTimerTicks);
            _isInfiniteTimeout = false;
        }

        //---------------------
        // Postconditions:None
    }

    // Reset timer to original duration.
    internal void Reset() {
        if (InfiniteTimeout == _originalTimerTicks) {
            _isInfiniteTimeout = true;
        } else {
            _timerExpire = checked(ADP.TimerCurrent() + _originalTimerTicks);
            _isInfiniteTimeout = false;
        }
    }

    //-------------------
    // Timeout info properties
    //-------------------

    // Indicator for infinite timeout when starting a timer
    internal static readonly long InfiniteTimeout = 0;

    // Is this timer in an expired state?
    internal bool IsExpired 
    {
        get 
        {
            return !IsInfinite && ADP.TimerHasExpired(_timerExpire);
        }
    }

    // is this an infinite-timeout timer?
    internal bool IsInfinite
    {
        get
        {
            return _isInfiniteTimeout;
        }
    }

    // Special accessor for TimerExpire for use when thunking to legacy timeout methods.
    internal long LegacyTimerExpire
    {
        get
        {
            return (_isInfiniteTimeout) ? Int64.MaxValue : _timerExpire;
        }
    }

    // Returns milliseconds remaining trimmed to zero for none remaining
    //  and long.MaxValue for infinite
    // This method should be prefered for internal calculations that are not
    //  yet common enough to code into the TimeoutTimer class itself.
    internal long MillisecondsRemaining
    {
        get 
        {
            //-------------------
            // Preconditions: None

            //-------------------
            // Method Body
            long milliseconds;
            if (_isInfiniteTimeout)
            {
                milliseconds = long.MaxValue;
            }
            else
            {
                milliseconds = ADP.TimerRemainingMilliseconds(_timerExpire);
                if (0 > milliseconds)
                {
                    milliseconds = 0;
                }
            }

            //--------------------
            // Postconditions
            Debug.Assert(0<=milliseconds); // This property guarantees no negative return values

            return milliseconds;
        }
    }

}

}

